from django.shortcuts import render
from django import http
from django.views import View
from apps.users.models import User
import logging,json, re
from django.contrib.auth import login, authenticate, logout
from django_redis import get_redis_connection
from meiduo_mall.utils.views import LoginRequiredJSONMixin
from celery_tasks.email.tasks import send_verify_email
from apps.users.models import Address
# Create your views here.


# 日志输出器
logger = logging.getLogger('django')


class ChangePasswordView(View):
    """修改密码
     PUT: /password/
     """
    def put(self,request):
        """实现修改密码逻辑"""

        # 接收参数
        json_dict = json.loads(request.body.decode())
        old_password = json_dict.get('old_password')
        new_password = json_dict.get('new_password')
        new_password2 = json_dict.get('new_password2')

        # 校验参数
        if not all([old_password,new_password,new_password2]):
            return http.JsonResponse({'code': 400, 'errmsg': '缺少必传参数'})

        result = request.user.check_password(old_password)
        if not result:
            return http.JsonResponse({'code': 400, 'errmsg': '原始密码不正确'})

        if not re.match(r'^[a-zA-Z0-9]{8,20}$', new_password):
            return http.JsonResponse({'code': 400, 'errmsg': '新密码格式错误'})

        if new_password != new_password2:
            return http.JsonResponse({'code': 400, 'errmsg': '两次输入密码不一致'})

        # 修改密码
        try:
            # from django.contrib.auth.hashers import make_password
            # request.user.password = make_password(new_password)
            request.user.set_password(new_password)
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': 400, 'errmsg': '修改密码失败'})

        # 清理状体保持信息
        logout(request)

        response = http.JsonResponse({'code': 0, 'errmsg': 'ok'})
        response.delete_cookie('username')

        # 响应密码修改结果，重定向到登录界面
        return response



class UpdateTitleAddressView(View):
    """设置地址标题
    PUT: /addresses/(?P<address_id>\d+)/title/
    """
    def put(self,request,address_id):
        """修改地址标题"""

        # 获取参数
        address = Address.objects.get(id=address_id)
        json_dict = json.loads(request.body.decode())
        title = json_dict.get('title')

        if not title:
            return http.JsonResponse({'code': 400, 'errmsg': '缺少必传参数'})

        try:
            # 修改地址标题
            address.title = title
            address.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': 400, 'errmsg': '设置地址标题失败'})

        # 返回响应结果
        return http.JsonResponse({'code': 0, 'errmsg': 'ok'})




class DefaultAddressView(View):
    """设置默认地址
    PUT: /addresses/(?P<address_id>\d+)/default/
    """
    def put(self,request,address_id):
        """设置默认地址"""
        try:
            #  接收参数，查询地址
            address = Address.objects.get(id=address_id)

            # 设置地址为默认地址
            request.user.default_address = address
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': 400, 'errmsg': '设置默认地址失败'})

        # 响应社会默认地址结果
        return http.JsonResponse({'code': 0, 'errmsg': 'ok'})



class UpdateDestroyAddressView(View):
    """修改和删除地址
    PUT: /addresses/(?P<address_id>\d+)/"""

    def put(self,request,address_id):
        """修改地址"""

        # 接受参数
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验参数
        if not all([receiver,province_id,city_id,district_id,place,mobile]):
            return http.JsonResponse({'code': 400, 'errmsg': '缺少必传参数'})

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.JsonResponse({'code': 400, 'errmsg': '参数mobile有误'})

        if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
            return http.JsonResponse({'code': 400, 'errmsg': '参数tel有误'})

        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.JsonResponse({'code': 400, 'errmsg': '参数email有误'})

        # 判断地址是否存在，并更新地址信息
        try:
            Address.objects.filter(id=address_id).update(
                user =request.user,
                title = receiver,
                receiver = receiver,
                province_id = province_id,
                city_id = city_id,
                district_id = district_id,
                place = place,
                mobile = mobile,
                tel = tel,
                email = email
            )
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': 400, 'errmsg': '更新地址失败'})

        # 构造响应数据
        address = Address.objects.get(id=address_id)

        address_dict = {
            'id': address.id,
            'title': address.title,
            'receiver': address.receiver,
            'province': address.province.name,
            'city': address.city.name,
            'district': address.district.name,
            'place':address.place,
            'mobile': address.mobile,
            'tel': address.tel,
            'email': address.email
        }

        # 响应更新地址结果
        return http.JsonResponse({'code': 0,
                                  'errmsg': 'ok',
                                  'address': address_dict})

    def delete(self,request,address_id):
        """删除地址
        逻辑删除"""
        try:
            # 查询要删除的地址
            address = Address.objects.get(id=address_id)

            # 将地址逻辑删除设置为True
            address.is_deleted = True
            address.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': 400, 'errmsg': '删除地址失败'})

        # 响应删除地址结果
        return http.JsonResponse({'code': 0, 'errmsg': 'ok'})



class AddressView(View):
    """用户收货地址
    GET: /addresses/"""

    def get(self,request):
        """提供地址管理界面"""

        # 获取所有的地址
        addresses = Address.objects.filter(user=request.user, is_deleted=False)

        # 创建空的列表
        address_dict_list = []

        # 遍历
        for address in addresses:
            address_dict = {
                'id': address.id,
                'title': address.title,
                'receiver': address.receiver,
                'province': address.province.name,
                'city': address.city.name,
                'district': address.district.name,
                'place': address.place,
                'mobile': address.mobile,
                'tel': address.tel,
                'email': address.email
            }

            # 将默认地址移动到最前面
            default_address = request.user.default_address
            if default_address.id == address.id:
                # 查询集 addresses 没有 insert方法
                address_dict_list.insert(0,address_dict)
            else:
                address_dict_list.append(address_dict)


        default_id = request.user.default_address_id

        return http.JsonResponse({'code': 0,
                                  'errmsg': 'ok',
                                  'addresses': address_dict_list,
                                  'default_address_id': default_id})



class CreateAddressView(View):
    """新增地址"""

    def post(self, request):
        """实现新增地址逻辑
        POST: /addresses/create/"""

        # 获取地址个数
        try:
            count = Address.objects.filter(user=request.user,
                                           is_deleted=False).count()

        except Exception as e:
            return http.JsonResponse({'code': 400, 'errmsg': '获取地址数据出错'})

        # 判断是否超过地址上线，最多20个
        if count >= 20:
            return http.JsonResponse({'code': 400, 'errmsg': '超过地址数量上线'})

        # 接受参数
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验参数
        if not all([receiver,province_id,city_id,district_id,place,mobile]):
            return http.JsonResponse({'code': 400, 'errmsg': '缺少必传参数'})

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.JsonResponse({'code': 400, 'errmsg': '参数mobile有误'})

        if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
            return http.JsonResponse({'code': 400, 'errmsg': '参数tel有误'})

        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.JsonResponse({'code': 400, 'errmsg': '参数email有误'})

        # 保存地址信息
        try:
            address = Address.objects.create(
                user = request.user,
                title = receiver,
                receiver = receiver,
                province_id = province_id,
                city_id = city_id,
                district_id = district_id,
                place = place,
                mobile = mobile,
                tel = tel,
                email = email
            )

            # 设置默认地址
            if not request.user.default_address:
                request.user.default_address = address
                request.user.save()

        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': 400, 'errmsg': '新增地址失败'})

        # 新增地址成功，将新增的地址响应给前段实现局部刷新
        address_dict = {
            'id': address.id,
            'title': address.title,
            'receiver': address.receiver,
            'province': address.province.name,
            'city': address.city.name,
            'district': address.district.name,
            'place': address.place,
            'mobile': address.mobile,
            'tel': address.tel,
            'email': address.email
        }

        # 响应保存结果
        return http.JsonResponse({'code': 0,
                                  'errmsg': 'ok',
                                  'address': address_dict})




class VerifyEmailView(View):
    """验证邮箱
    PUT： /emails/verification/"""

    def put(self,request):
        """实现邮箱验证逻辑"""
        # 接受参数
        token = request.GET.get('token')

        # 校验参数，判断token 是都为空或者过期，提取user
        if not token:
            return http.JsonResponse({'code': 400, 'errsmg': '缺少token'})

        # 调用上面封装好的方法，将token传入
        user = User.check_verify_email_token(token)
        if not user:
            return http.JsonResponse({'code': 400, 'errmsg': '无效的token'})

        # 修改email_active 的值为True
        try:
            user.email_active = True
            user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': 400, 'errmsg': '激活邮件失败'})

        # 返回邮箱验证结果
        return http.JsonResponse({'code': 0, 'errmsg': 'ok'})



class EmailView(View):
    """添加邮箱
    PUT: /emails/"""

    def put(self,request):
        """实现添加邮箱逻辑"""

        # 接收参数
        json_dict = json.loads(request.body.decode())
        email = json_dict.get('email')


        # 校验email
        if not email:
            return http.JsonResponse({'code': 400, 'errmsg': '缺少必传参数'})

        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return http.JsonResponse({'code': 400, 'errmsg': '邮箱格式不对'})

        # 赋值email字段
        try:
            request.user.email = email
            request.user.save()
        except Exception as e:
            return http.JsonResponse({'code': 400, 'errmsg': '添加邮箱失败'})

        # 生成邮箱验证链接
        verify_url = request.user.generate_verify_email_url()

        # 发送验证链接
        send_verify_email.delay(email,verify_url)



        return http.JsonResponse({'code': 0, 'errmsg': 'ok'})


class UserInfoView(LoginRequiredJSONMixin, View):
    """用户中心
    GET:info/
    """
    def get(self,request):
        """实现用户中心数据展示"""

        # 获取界面需要的数据，进行拼接
        data_dict = {
            'code': 0,
            'errnsg': 'ok',
            'info_data': {
                'username': request.user.username,
                'mobile': request.user.mobile,
                'email': request.user.email,
                'email_active': request.user.email_active,
            }
        }

        return http.JsonResponse(data_dict)



class LogoutView(View):
    """退出登录
    DELETE:logout/
    """

    def delete(self,request):
        """实现退出登录逻辑
        提示：
            退出登录的逻辑正好和登录的逻辑相反
            如果登录成功，记住登录状态，那么退出登录就是清理登录状态
            登录成功后，将username写入cookie，那么退出登录就要清楚username这个cookie
        """

        # 清理登录状态
        logout(request)

        # 清理username这个cookie
        response = http.JsonResponse({'code': 0,'errmsg': 'ok'})

        response.delete_cookie('username')

        return response




class LoginView(View):
    """用户登录
    POST:/login/
    """

    def post(self,request):
        # 提取参数
        json_dict = json.loads(request.body.decode())
        username = json_dict.get('username')
        password = json_dict.get('password')
        # 这个参数不用校验，因为不管存不存在都不影响登录
        remembered = json_dict.get('remembered')

        # 校验参数是否传完整
        if not all([username,password]):
            return http.JsonResponse({'code': 400,
                                      'errmsg': '缺少必传参数'})

        # 校验用户名username
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return http.JsonResponse({'code': 400,
                                      'errmsg': 'username格式错误'})

        # 校验password
        if not re.match(r'^[0-9a-zA-Z]{8,20}$', password):
            return http.JsonResponse({'code': 400,
                                      'errmsg': 'password格式错误'})


        # Django封装好的：在数据库中查找用户,如果没找到返回None
        user = authenticate(request=request,username=username,password=password)

        # 判断用户名和密码是否正确
        if user is None:
            return http.JsonResponse({'code': 400,
                                      'errmsg': '用户名或密码错误'})

        # 状态保持
        login(request, user)

        # 判断用户是否勾选了记住登录状态
        if remembered:
            # 如果勾选了，则保持登录状态2周，None 默认两周
            request.session.set_expiry(None)
        else:
            # 如果没有勾选，则在网页结束时不保存登录状态
            request.session.set_expiry(0)

        # 设置响应对象
        response = http.JsonResponse({'code': 0, 'errmsg': 'ok'})

        # 给响应对象设置cookie，发送给前端，让前端进行首页用户名展示
        response.set_cookie('username',user.username, max_age=3600*24*14)
        return response



class RegisterView(View):
    """用户注册
    POST: http://www.meiduo.site:8000/register
    """
    def post(self, request):
        """实现注册逻辑"""
        # 接受参数，请求体中的JSON数据，request.body
        # json_bytes = request.body     # # 从请求体中获取原始的JSON数据，是bytes类型
        #
        # json_str = json_bytes.decode()   # 将bytes类型的JSON数据，转成JSON字符串
        # json_dict = json.loads(json_str)   # 把JSON字符串转成python的标准字典

        json_dict =json.loads(request.body.decode())

        # 提取参数
        username = json_dict.get('username')
        password = json_dict.get('password')
        password2 = json_dict.get('password2')
        mobile = json_dict.get('mobile')
        allow = json_dict.get('allow')
        sms_code_client = json_dict.get('sms_code')

        # 整体校验，判断参数是否完整
        if not all([username,password,password2,mobile,allow,sms_code_client]):
            return http.JsonResponse({'code': 400,
                                      'errmsg': '缺少必传参数'})

        # username检测
        if not re.match(r'^[a-zA-z0-9_-]{5,20}$', username):
            return http.JsonResponse({'code': 400,
                                      'errmsg': 'username格式有误'})

        # password检测
        if not re.match(r'^[a-zA-Z0-9]{8,20}$', password):
            return http.JsonResponse({'code': 400,
                                      'errmsg': 'password格式有误'})

        # 检测password和password2是否相同
        if password != password2:
            return http.JsonResponse({'code': 400,
                                      'errmsg': '两次输入不相同'})

        # mobile检验
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.JsonResponse({'code': 400,
                                      'errmsg': 'mobile格式错误'})

        # allow检验
        if allow != True:
            return http.JsonResponse({'code': 400,
                                      'errmsg': 'allow格式有误'})

        # sms_code检验
        # 1.链接redis数据库
        redis_conn = get_redis_connection('verify_code')

        # 2.从redis中取值
        sms_code_server = redis_conn.get('sms_%s' % mobile)

        # 3.判断该值是否存在
        if not sms_code_server:
            return http.JsonResponse({'code': 400,
                                      'errmsg': '短信验证码过期'})

        # 4.对比redis中取出的值和前端发的值
        if sms_code_client != sms_code_server.decode():
            return http.JsonResponse({'code': 400,
                                      'errmsg': '验证码错误'})

        # 保存到数据库（username,password,mobile）
        try:
            user = User.objects.create_user(username=username,
                                            password=password,
                                            mobile=mobile)
        except Exception as e:
            return http.JsonResponse({'code': 400,
                                      'errmsg': '保存到数据库出错'})

        # 实现状态保持
        login(request, user)

        # 设置响应对象
        response = http.JsonResponse({'code': 0, 'errmsg': 'ok'})

        # 给响应对象设置cookie，发送给前端，让前端进行首页用户名展示
        response.set_cookie('username', user.username, max_age=3600 * 24 * 14)
        return response




class UsernameCountView(View):
    """判断用户名是否重复注册"""

    def get(self, request, username):
        '''判断用户名是否重复'''
        # 1.查询username在数据库中的个数
        try:
            count = User.objects.filter(username=username).count()
        except Exception as e:
            return http.JsonResponse({'code':400,
                                 'errmsg':'访问数据库失败'})

        # 2.返回结果(json) ---> code & errmsg & count
        return http.JsonResponse({'code': 0,
                             'errmsg': 'ok',
                             'count':count})


class MobileCountView(View):

    def get(self, request, mobile):
        '''判断手机号是否重复注册'''
        # 1.查询mobile在mysql中的个数
        try:
            count = User.objects.filter(mobile=mobile).count()
        except Exception as e:
            return http.JsonResponse({'code':400,
                                 'errmsg':'查询数据库出错'})

        # 2.返回结果(json)
        return http.JsonResponse({'code':0,
                             'errmsg':'ok',
                             'count':count})